﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using OSGeo.MapGuide;
using System.Xml;
using System.Data;

namespace IGIS.Core
{
    public class Feature : ICloneable
    {
        // Fields
        private DataSet _dataSet;
        private HtzMgHelper _helper;
        private string _identityFieldName;
        private readonly string _uidKey;

        // Methods
        private Feature()
        {
            this._identityFieldName = "FeatId";
        }

        public Feature(HtzMgHelper helper, string layerName, string filter)
            : this(helper, layerName, filter, "UID")
        {
        }

        public Feature(HtzMgHelper helper, string layerName, string filter, string uidKey)
        {
            this._identityFieldName = "FeatId";
            this._uidKey = string.IsNullOrEmpty(uidKey) ? "UID" : uidKey;
            this._helper = helper;
            this.Layer = this._helper.Map.GetLayers().GetItem(layerName) as MgLayer;
            this.Properties = new Dictionary<string, FeatureProperty>();
            this.GetFeatureFields();
            this.GetAdditionalValues();
            this.GetOrderValues();
        }

        public object Clone()
    {
        Feature feature = new Feature
        {
            Layer = this.Layer,
            _helper = this._helper,
            _dataSet = this._dataSet,
            _identityFieldName = this._identityFieldName,
            Properties = new Dictionary<string, FeatureProperty>()
        };
       
        foreach (KeyValuePair<string, FeatureProperty> p in this.Properties)
        {
            feature.Properties.Add(p.Key, (FeatureProperty) p.Value.Clone());
        }
        return feature;
    }

        public void FillLookUpValues()
        {
            string fileName = this._helper.GetSdfFile(this.Layer.FeatureSourceId);
            if (!string.IsNullOrEmpty(fileName))
            {
                foreach (KeyValuePair<string, FeatureProperty> p in this.Properties)
                {
                    if (p.Key.StartsWith("FK_"))
                    {
                        string className = p.Key.Split(new char[] { '_' })[1];
                        SdfEditor.SdfEditor editor = new SdfEditor.SdfEditor();
                       
                        XmlDocument doc = new XmlDocument();
                        doc.LoadXml(editor.NfcAllEntriesToXmlWithCode(fileName, className, "Code"));
                        XmlNodeList nodes = doc.GetElementsByTagName("entry");
                        if (nodes.Count > 0)
                        {
                            p.Value.LookUpValues = new Dictionary<int, string>();
                            p.Value.IsLookupWithCode = p.Value.LookUpValues.ContainsKey(-1) && (p.Value.LookUpValues[-1] == "yes");
                            p.Value.LookUpValues.Remove(-1);
                            foreach (XmlNode n in nodes)
                            {
                                int key = Convert.ToInt32(n.SelectSingleNode("key").InnerText);
                                string value = n.SelectSingleNode("value").InnerText;
                                p.Value.LookUpValues.Add(key, value);
                            }
                        }
                    }
                }
            }
        }

        public void FillValues(MgFeatureReader reader)
        {
            List<string> toDelete = new List<string>();
            try
            {
                this.FeatId = reader.GetInt32(this._identityFieldName);
                this.UID = reader.GetInt32(this._uidKey);
                this.HaveUID = true;
            }
            catch
            {
            }
            foreach (KeyValuePair<string, FeatureProperty> item in this.Properties)
            {
                try
                {
                    item.Value.Value = this._helper.GetFeaturePropertyValue(reader, item.Key, false);
                    item.Value.PropertyType = reader.GetPropertyType(item.Key);
                    item.Value._initialized = true;
                }
                catch (MgObjectNotFoundException)
                {
                    toDelete.Add(item.Key);
                }
            }
            foreach (string key in toDelete)
            {
                this.Properties.Remove(key);
            }
            try
            {
                this.GetAviableValues(reader.ToXml().ToString());
            }
            catch
            {
            }
        }

        public void FillValues(string filter)
        {
            MgFeatureQueryOptions queryOptions = new MgFeatureQueryOptions();
            queryOptions.SetFilter(filter);
            MgFeatureReader reader = this.Layer.SelectFeatures(queryOptions);
            reader.ReadNext();
            this.FillValues(reader);
        }

        public DataSet GenerateDataSet()
    {
        DataSet dsFeature = new DataSet("dsFeature");
        DataTable dtFeature = new DataTable("dtFeature");
        List<DataColumn> pk = new List<DataColumn>();
        foreach (KeyValuePair<string, FeatureProperty> property in this.Properties)
        {
            if ((property.Value.Value == null) || (property.Value.Value.ToString().IndexOf("<a ") < 0))
            {
                DataColumn dc = new DataColumn(property.Value.Name) {
                    Caption = property.Value.Alias,
                    ReadOnly = property.Value.IsReadOnly
                };
                
                dtFeature.Columns.Add(dc);
                if (property.Value.IsIdentity)
                {
                    pk.Add(dc);
                }
            }
        }
        dtFeature.PrimaryKey = pk.ToArray();
        DataRow r = dtFeature.NewRow();
        foreach (KeyValuePair<string, FeatureProperty> property in from property in this.Properties
            where (property.Value.Value ?? string.Empty).ToString().IndexOf("<a ") < 0
            select property)
        {
            r.SetField<object>(property.Value.Name, property.Value.Value);
        }
        dtFeature.Rows.Add(r);
        dsFeature.Tables.Add(dtFeature);
        return dsFeature;
    }

        private void GetAdditionalValues()
        {
            string[] schemaClass = this.Layer.FeatureClassName.Split(new char[] { ':' });
            MgClassDefinition classDef = this._helper.FeatureService.GetClassDefinition(new MgResourceIdentifier(this.Layer.FeatureSourceId), schemaClass[0], schemaClass[1]);
            this._identityFieldName = classDef.GetIdentityProperties()[0].Name;
            MgPropertyDefinitionCollection dpdCollection = classDef.GetProperties();
            foreach (MgDataPropertyDefinition dpd in (from t in dpdCollection
                                                      where t.PropertyType == 100
                                                      select t).Cast<MgDataPropertyDefinition>())
            {
                if ((dpd.Description == "FieldID") || (dpd.Description == "מזהה מערכת"))
                {
                    if (!this.Properties.ContainsKey(dpd.Name))
                    {
                        this.Properties.Add(dpd.Name, new FeatureProperty(dpd.Name, dpd.Name));
                        this.Properties[dpd.Name].IsDisplayed = false;
                    }
                    this.Properties[dpd.Name].IsReadOnly = true;
                    this.Properties[dpd.Name].IsIdentity = true;
                    this._identityFieldName = dpd.Name;
                }
                else if (this.Properties.ContainsKey(dpd.Name))
                {
                    this.Properties[dpd.Name].IsReadOnly = dpd.ReadOnly;
                }
            }
        }

        private void GetAviableValues(string xmlSchema)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xmlSchema);
            XmlNamespaceManager nsmr = new XmlNamespaceManager(doc.NameTable);
            nsmr.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema");
            XmlNodeList elements = doc.GetElementsByTagName("xs:element");
            foreach (XmlNode element in elements)
            {
                XmlNodeList enumeration = element.SelectNodes("xs:simpleType/xs:restriction/xs:enumeration", nsmr);
                if (enumeration.Count > 0)
                {
                    string propertyName = enumeration[0].ParentNode.ParentNode.ParentNode.Attributes["name"].Value;
                    if (this.Properties.ContainsKey(propertyName))
                    {
                        this.Properties[propertyName].AviableValues = new List<string>();
                        foreach (XmlNode node in enumeration)
                        {
                            this.Properties[propertyName].AviableValues.Add(node.Attributes["value"].Value);
                        }
                    }
                }
            }
        }

        private void GetFeatureFields()
        {
            MgByteReader reader = this._helper.ResourceService.GetResourceContent(this.Layer.LayerDefinition);
            string xmlSchema = reader.ToString();
            reader.Dispose();
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlSchema);
            XmlNodeList xmlProperties = xmlDoc.GetElementsByTagName("PropertyMapping");
            foreach (XmlNode node in xmlProperties)
            {
                string Name = node.SelectSingleNode("Name").InnerText;
                string Alias = node.SelectSingleNode("Value").InnerText;
                this.Properties.Add(Name, new FeatureProperty(Name, Alias));
                this.Properties[Name].IsDisplayed = true;
            }
        }

        public static List<Feature> GetFeatures(HtzMgHelper helper, MgSelection selection, string layerName)
        {
            MgFeatureReader reader;
            List<Feature> result = new List<Feature>();
            MgLayer layer = (MgLayer)helper.Map.GetLayers().GetItem(layerName);
            if ((selection != null) && (selection.GetLayers() != null))
            {
                string filter = selection.GenerateFilter(layer, layer.FeatureClassName);
                MgFeatureQueryOptions opt = new MgFeatureQueryOptions();
                opt.SetFilter(filter);
                reader = helper.FeatureService.SelectFeatures(new MgResourceIdentifier(layer.FeatureSourceId), layer.FeatureClassName, opt);
            }
            else
            {
                reader = helper.FeatureService.SelectFeatures(new MgResourceIdentifier(layer.FeatureSourceId), layer.FeatureClassName, null);
            }
            if (reader.ReadNext())
            {
                Feature prototype = new Feature(helper, layerName, "");
                Feature f1 = prototype.Clone() as Feature;
                f1.FillValues(reader);
                result.Add(f1);
                while (reader.ReadNext())
                {
                    Feature f = prototype.Clone() as Feature;
                    f.FillValues(reader);
                    result.Add(f);
                }
            }
            return result;
        }

        public static List<Feature> GetFeatures(HtzMgHelper helper, string filter, string layerName)
        {
            List<Feature> result = new List<Feature>();
            MgLayer layer = helper.Map.GetLayers().GetItem(layerName) as MgLayer;
            Feature prototype = new Feature(helper, layerName, "");
            MgFeatureQueryOptions opt = new MgFeatureQueryOptions();
            if (!string.IsNullOrEmpty(filter))
            {
                opt.SetFilter(filter);
            }
            MgFeatureReader reader = helper.FeatureService.SelectFeatures(new MgResourceIdentifier(layer.FeatureSourceId), layer.FeatureClassName, opt);
            while (reader.ReadNext())
            {
                Feature f = prototype.Clone() as Feature;
                f.FillValues(reader);
                result.Add(f);
            }
            return result;
        }

        private void GetOrderValues()
        {
            string schemaName = this.Layer.FeatureClassName.Split(new char[] { ':' })[0];
            string filter = string.Format("FEAT_CLASS_NAME = '{0}'", this.Layer.FeatureClassName.Split(new char[] { ':' })[1]);
            MgFeatureQueryOptions opt = new MgFeatureQueryOptions();
            opt.SetFilter(filter);
            if (this._helper.FeatureService.GetClasses(new MgResourceIdentifier(this.Layer.FeatureSourceId), schemaName).Contains(schemaName + ":FIELDS_ORDER"))
            {
                MgFeatureReader reader = this._helper.FeatureService.SelectFeatures(new MgResourceIdentifier(this.Layer.FeatureSourceId), "FIELDS_ORDER", opt);
                while (reader.ReadNext())
                {
                    string propertyName = reader.GetString("PROPERTY_NAME");
                    int orderId = reader.GetInt32("ORDER_ID");
                    if (this.Properties.ContainsKey(propertyName))
                    {
                        this.Properties[propertyName].OrderId = orderId;
                    }
                }
            }
        }

        public static Feature GetSingleFeature(HtzMgHelper helper, MgSelection selection, string layerName)
        {
            return GetSingleFeature(helper, selection, layerName, "UID");
        }

        public static Feature GetSingleFeature(HtzMgHelper helper, string filter, string layerName)
        {
            return GetSingleFeature(helper, filter, layerName, "UID");
        }

        public static Feature GetSingleFeature(HtzMgHelper helper, MgSelection selection, string layerName, string uidKey)
        {
            Feature result = null;
            MgLayer layer = helper.Map.GetLayers().GetItem(layerName) as MgLayer;
            if ((selection != null) && (selection.GetLayers() != null))
            {
                string filter = selection.GenerateFilter(layer, layer.FeatureClassName);
                result = GetSingleFeature(helper, filter, layerName, uidKey);
            }
            return result;
        }

        public static Feature GetSingleFeature(HtzMgHelper helper, string filter, string layerName, string uidKey)
        {
            Feature result = null;
            if (helper.Map.GetLayers().Contains(layerName))
            {
                MgLayer layer = (MgLayer)helper.Map.GetLayers().GetItem(layerName);
                if (string.IsNullOrEmpty(filter))
                {
                    return result;
                }
                MgFeatureQueryOptions opt = new MgFeatureQueryOptions();
                opt.SetFilter(filter);
                MgFeatureReader reader = helper.FeatureService.SelectFeatures(new MgResourceIdentifier(layer.FeatureSourceId), layer.FeatureClassName, opt);
                if (reader.ReadNext())
                {
                    result = new Feature(helper, layerName, "", uidKey);
                    result.FillValues(reader);
                    result.FillLookUpValues();
                }
            }
            return result;
        }

        public void UpdateFeature(DataSet ds)
        {
            string filter = string.Empty;
            foreach (KeyValuePair<string, FeatureProperty> item in this.Properties)
            {
                item.Value.Value = ds.Tables[0].Rows[0].Field<string>(item.Key);
                if (item.Value.IsIdentity)
                {
                    filter = string.Format("{0}={1}", item.Key, item.Value.Value);
                }
            }
            this.UpdateFeature(filter);
        }

        public void UpdateFeature(string filter)
        {
            MgResourceIdentifier featureResId = new MgResourceIdentifier(this.Layer.GetFeatureSourceId());
            MgClassDefinition classDefinition = this._helper.FeatureService.GetClassDefinition(featureResId, this.Layer.FeatureClassName.Split(new char[] { ':' })[0], this.Layer.FeatureClassName.Split(new char[] { ':' })[1]);
            Dictionary<string, Dictionary<string, FeatureProperty>> propCollection = new Dictionary<string, Dictionary<string, FeatureProperty>>();
            string attributeClassProperty = string.Empty;
            string attributeClass = string.Empty;
            string attributeResourceId = string.Empty;
            string featureClassPropertyValue = string.Empty;
            try
            {
                XmlNodeList extensions = ResourceHelper.GetResourceXml(this._helper, featureResId).GetElementsByTagName("Extension");
                XmlNode attrRelateNode = extensions[0].SelectSingleNode("AttributeRelate");
                if ((attrRelateNode != null) && (extensions.Count > 0))
                {
                    string featureClassProperty = attrRelateNode.SelectSingleNode("RelateProperty/FeatureClassProperty").InnerText;
                    attributeClassProperty = attrRelateNode.SelectSingleNode("RelateProperty/AttributeClassProperty").InnerText;
                    attributeClass = attrRelateNode.SelectSingleNode("AttributeClass").InnerText;
                    attributeResourceId = attrRelateNode.SelectSingleNode("ResourceId").InnerText;
                    MgFeatureQueryOptions opt = new MgFeatureQueryOptions();
                    opt.SetFilter(filter);
                    MgFeatureReader reader = this._helper.FeatureService.SelectFeatures(featureResId, this.Layer.FeatureClassName, opt);
                    reader.ReadNext();
                    featureClassPropertyValue = this._helper.GetFeaturePropertyValue(reader, featureClassProperty).ToString();
                    reader.Close();
                    reader.Dispose();
                }
            }
            catch (Exception)
            {
            }
            foreach (MgPropertyDefinition item in classDefinition.GetProperties())
            {
                if (this.Properties.ContainsKey(item.Name))
                {
                    string[] strs = item.QualifiedName.Split(new char[] { '.' });
                    if (!propCollection.ContainsKey(strs[0]))
                    {
                        propCollection.Add(strs[0], new Dictionary<string, FeatureProperty>());
                    }
                    propCollection[strs[0]].Add(item.QualifiedName.Split(new char[] { '.' })[1], this.Properties[item.Name]);
                }
            }
            foreach (KeyValuePair<string, Dictionary<string, FeatureProperty>> item in propCollection)
            {
                string f = filter;
                MgResourceIdentifier resId = featureResId;
                if ((item.Key == attributeClass) && !string.IsNullOrEmpty(featureClassPropertyValue))
                {
                    f = string.Format("{0}={1}", attributeClassProperty, featureClassPropertyValue);
                    resId = new MgResourceIdentifier(attributeResourceId);
                }
                this.UpdatingFeature(resId, item.Key, f, item.Value);
            }
        }

        private void UpdatingFeature(MgResourceIdentifier featureResId, string featureClassName, string filter, Dictionary<string, FeatureProperty> propCollection)
        {
            MgFeatureCommandCollection updCommands = new MgFeatureCommandCollection();
            MgPropertyCollection properties = new MgPropertyCollection();
            MgPropertyCollection properties2Log = new MgPropertyCollection();
            if (this.InsertToLog)
            {
                properties2Log.Add(new MgInt32Property("ItemId", this.FeatId));
            }
            foreach (KeyValuePair<string, FeatureProperty> item in propCollection)
            {
                try
                {
                    MgProperty prop = item.Value.GenerateProperty();
                    MgProperty prop4Log = item.Value.GenerateProperty(false);
                    if (prop4Log != null)
                    {
                        prop4Log.Name = item.Key;
                        if (this.InsertToLog)
                        {
                            properties2Log.Add(prop4Log);
                        }
                    }
                    if (prop != null)
                    {
                        prop.Name = item.Key;
                        properties.Add(prop);
                    }
                }
                catch (InvalidOperationException)
                {
                }
            }
            if (featureClassName == string.Empty)
            {
                featureClassName = this.Layer.FeatureClassName;
            }
            if (properties.Count > 0)
            {
                MgUpdateFeatures updateCommand = new MgUpdateFeatures(featureClassName, properties, filter);
                updCommands.Add(updateCommand);
                if (this.InsertToLog)
                {
                    MgInsertFeatures insert = new MgInsertFeatures(featureClassName + "Log", properties2Log);
                    updCommands.Add(insert);
                }
                this._helper.FeatureService.UpdateFeatures(featureResId, updCommands, false);
                this._helper.Map.Save(this._helper.ResourceService);
            }
        }

        // Properties
        public int FeatId { get; private set; }

        public DataSet FeatureDataSet
        {
            get
            {
                this._dataSet = this.GenerateDataSet();
                return this._dataSet;
            }
        }

        public bool HaveUID { get; private set; }

        public bool InsertToLog { get; set; }

        public MgLayer Layer { get; private set; }

        public string LegendLabel
        {
            get
            {
                return this.Layer.LegendLabel;
            }
        }

        public Dictionary<string, FeatureProperty> Properties { get; private set; }

        public int UID { get; set; }
    }

 

}